﻿namespace HZY.Repository.EntityFramework.Attributes;

/// <summary>
/// DbContext 配置注解
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class DbContextConfigAttribute : Attribute
{
    /// <summary>
    /// 命名规则默认 蛇形 命名 SysFunction => sys_function
    /// </summary>
    public NameRuleType NameRuleType { get; set; } = NameRuleType.SnakeCase;

    /// <summary>
    /// 实体命名空间
    /// </summary>
    public string EntityNamespace { get; set; }

    public DbContextConfigAttribute(string entityNamespace)
    {
        EntityNamespace = entityNamespace;
    }

    public DbContextConfigAttribute(string entityNamespace, NameRuleType nameRuleType)
    {
        EntityNamespace = entityNamespace;
        NameRuleType = nameRuleType;
    }

    /// <summary>
    /// 获取模型类型集合
    /// </summary>
    /// <param name="dbContextType"></param>
    /// <param name="predicate"></param>
    /// <returns></returns>
    public List<Type> GetModelTypes(Type dbContextType, Func<Type, bool>? predicate = null)
    {
        var assembly = dbContextType.Assembly;
        var types = (from w in assembly.ExportedTypes.ToList()
                     where w.IsClass && w.IsPublic && !w.IsGenericType
                     where w.GetInterface(nameof(IEntity)) != null
                     //where !w.Name.StartsWith(nameof(DefaultEntity)) && !w.Name.Contains(nameof(FullEntity))
                     select w)
                    .WhereIf(predicate != null, predicate)
                    .Where(w => Regex.IsMatch(w.FullName!, EntityNamespace))
                    .ToList()
                    ;

        //扫描类型下面的 dbset model
        var propertyInfos = dbContextType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        var dbsets = propertyInfos.Where(w => w.PropertyType.Name == "DbSet`1");

        foreach (var dbset in dbsets)
        {
            if (dbset.PropertyType.GenericTypeArguments.Length <= 0) continue;

            var model = dbset.PropertyType.GenericTypeArguments[0];

            if (types.Any(w => w.FullName == model.FullName)) continue;

            types.Add(model);
        }

        return types;
    }

    /// <summary>
    /// 模型创建
    /// </summary>
    /// <param name="modelBuilder"></param>
    /// <param name="modelTypes"></param>
    /// <returns></returns>
    public List<Type> OnModelCreating(ModelBuilder modelBuilder, List<Type> modelTypes)
    {
        #region 自动注册 dbset

        var types = modelTypes;

        foreach (var type in types)
        {
            if (modelBuilder.Model.FindEntityType(type) != null)
            {
                continue;
            }

            modelBuilder.Model.AddEntityType(type);
        }

        #endregion

        #region 过滤软删除 条件是：实体必须继承自 IDeleteBaseEntity

        var deleteBaseEntitys = modelBuilder.Model
            .GetEntityTypes()
            .Where(w => typeof(IDeleteEntity).IsAssignableFrom(w.ClrType))
            ;
        foreach (var item in deleteBaseEntitys)
        {
            var lambda = ExpressionTreeExtensions.Equal(item.ClrType, nameof(IDeleteEntity.IsDeleted), false);
            modelBuilder.Entity(item.ClrType).HasQueryFilter(lambda);
        }

        #endregion

        #region 自动映射表名
        NamingConvention(modelBuilder);
        #endregion

        return types;
    }

    /// <summary>
    /// 命名规则转化
    /// </summary>
    protected void NamingConvention(ModelBuilder modelBuilder)
    {
        foreach (var entity in modelBuilder.Model.GetEntityTypes())
        {
            NamingConventionToTableName(entity);
            NamingConventionToFieldName(entity);
        }
    }

    /// <summary>
    /// 表名称命名规则
    /// </summary>
    /// <param name="mutableEntityType"></param>
    protected void NamingConventionToTableName(IMutableEntityType mutableEntityType)
    {
        var _type = mutableEntityType.ClrType;

        var tableAttribute = _type.GetCustomAttribute<TableAttribute>();
        var entityDescriptionAttribute = _type.GetCustomAttribute<EntityDescriptionAttribute>();

        if (tableAttribute == null)
        {
            var oldName = mutableEntityType.GetTableName() ?? _type.Name;
            var nameRuleType = entityDescriptionAttribute == null ? NameRuleType : entityDescriptionAttribute.NameRuleType;
            mutableEntityType.SetTableName(GetNameByNameRuleType(oldName, nameRuleType));
            return;
        }

        //mutableEntityType.SetTableName(tableAttribute.Name);
    }

    /// <summary>
    /// 字段名称命名规则
    /// </summary>
    /// <param name="mutableEntityType"></param>
    protected void NamingConventionToFieldName(IMutableEntityType mutableEntityType)
    {
        var _type = mutableEntityType.ClrType;

        var entityDescriptionAttribute = _type.GetCustomAttribute<EntityDescriptionAttribute>();

        if (entityDescriptionAttribute != null && entityDescriptionAttribute.FieldIgnored) return;

        var mutableProperties = mutableEntityType.GetProperties();

        foreach (var item in mutableProperties)
        {
            if (item.PropertyInfo is null)
            {
                continue;
            }

            var columnAttribute = item.PropertyInfo.GetCustomAttribute<ColumnAttribute>();
            if (columnAttribute is not null && !string.IsNullOrWhiteSpace(columnAttribute.Name))
            {
                continue;
            }

            var nameRuleType = entityDescriptionAttribute == null ? NameRuleType : entityDescriptionAttribute.NameRuleType;
            var oldColumnName = item.GetColumnName();
            var newColumnName = GetNameByNameRuleType(oldColumnName, nameRuleType);
            item.SetColumnName(newColumnName);
        }
    }

    /// <summary>
    /// 获取名称根据命名规则
    /// </summary>
    /// <param name="oldName"></param>
    /// <param name="nameRuleType"></param>
    /// <returns></returns>
    protected string GetNameByNameRuleType(string oldName, NameRuleType nameRuleType)
    {
        string newName = string.Empty;
        switch (nameRuleType)
        {
            case NameRuleType.Default:
                break;
            case NameRuleType.SnakeCase:
                // 蛇形命名法
                // ToUnderlineNomenclature()  将驼峰命名法改为蛇形命名法  类似: SysFunction => sys_function
                newName = oldName.ToUnderlineNomenclature();
                break;
            case NameRuleType.UpperSnakeCase:
                // 全大写蛇形命名法
                //  SysFunction => SYS_FUNCTION
                newName = oldName.ToUnderlineNomenclature().ToUpper();
                break;
            case NameRuleType.Upper:
                // 表名全大写
                newName = oldName.ToUpper();
                break;
            case NameRuleType.Lower:
                // 表名全小写
                newName = oldName.ToLower();
                break;
        }

        return newName;
    }


}
